﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using NHibernate.Criterion;
using Light.Utility;

namespace Light.Framework.Model
{
    /// <summary>
    /// 查询条件关系类型
    /// </summary>
    public enum SearchLogic
    {
        And,
        Or,
    }

    /// <summary>
    /// 查询条件运算符
    /// </summary>
    public enum SearchOperator
    {
        /// <summary>
        ///等于
        /// </summary>
        Eq,
        /// <summary>
        /// 不等于
        /// </summary>
        Not,
        /// <summary>
        /// 大于
        /// </summary>
        Gt,
        /// <summary>
        /// 小于
        /// </summary>
        Lt,
        /// <summary>
        /// 大于等于
        /// </summary>
        Ge,
        /// <summary>
        /// 小于等于
        /// </summary>
        Le,
    }

    /// <summary>
    /// 查询参数
    /// </summary>
    public class SearchArgs
    {

        public SearchArgs()
        {
            Extras = new Dictionary<string, object>();
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="keyword">输入的关键字</param>
        /// <param name="searchFields">搜索属性字段列表</param>
        public SearchArgs(string keyword, List<string> searchFields)
            : this()
        {
            if (!String.IsNullOrEmpty(keyword))
            {
                foreach (var field in searchFields)
                {
                    this[field] = keyword;
                }
            }
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="keyword">输入的关键字</param>
        /// <param name="searchFields">搜索属性字段字典</param>
        public SearchArgs(string keyword, Dictionary<string, string> searchFields)
            : this()
        {
            if (!String.IsNullOrEmpty(keyword))
            {
                foreach (var key in searchFields.Keys)
                {
                    this[searchFields[key]] = keyword;
                }
            }
        }

        #region private

        private SearchLogic logic = SearchLogic.Or;
        private Dictionary<string, object> fields = new Dictionary<string, object>();

        List<SearchExpression> expressions = new List<SearchExpression>();

        #endregion

        /// <summary>
        /// 设置搜索项，共用属性Logic
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public object this[string name]
        {
            get
            {
                if (fields.Keys.Contains(name))
                    return fields[name];
                else
                    return string.Empty;
            }
            set
            {
                if (fields.Keys.Contains(name))
                    fields[name] = value;
                else
                    fields.Add(name, value);
            }
        }

        /// <summary>
        /// 额外的查询项，调用Add方法的条件会存放于此，仅用于传值
        /// </summary>
        public Dictionary<string, object> Extras { get; set; }

        /// <summary>
        /// 日期：开始
        /// </summary>
        public DateTime DateStart { get; set; }

        /// <summary>
        /// 日期：结束
        /// </summary>
        public DateTime DateEnd { get; set; }

        /// <summary>
        /// 日期：字段名/属性名
        /// </summary>
        public string DateField { get; set; }

        /// <summary>
        ///查询条件逻辑运算符(AND,OR) 默认为OR
        /// </summary>
        public SearchLogic Logic
        {
            get { return logic; }
            set { logic = value; }
        }

        /// <summary>
        /// 逐一添加搜索条件，用单独的SearchLogic
        /// </summary>
        /// <param name="logic"></param>
        /// <param name="field"></param>
        /// <param name="value"></param>
        public void Add(SearchLogic logic, string field, object value)
        {

            SearchExpression expression = new SearchExpression(logic, field, value);
            expressions.Add(expression);

            Extras[field] = value;
        }

        public void Add(SearchLogic logic, string field, object value, SearchOperator optor)
        {

            SearchExpression expression = new SearchExpression(logic, field, value, optor);
            expressions.Add(expression);

            Extras[field] = value;
        }

        /// <summary>
        /// 转换成NHibernate的查询对象ICriterion
        /// </summary>
        /// <typeparam name="T">要查询的结果类型</typeparam>
        /// <returns>ICriterion</returns>
        public ICriterion ToCriterion<T>()
        {
            ICriterion criterion = null;
            ICriterion criterionDate = null;

            string entityName = typeof(T).Name;
            object value = null;
            string property = string.Empty;

            foreach (var key in this.fields.Keys)
            {
                property = key;
                value = this.fields[key];

                if (value == null || String.IsNullOrEmpty(value.ToString()))
                {
                    continue;
                }

                if (!Light.Utility.ReflectionHelper.TryChangeType<T>(property, ref value))
                {
                    continue;
                }
                else if (value.GetType() == typeof(String))
                {
                    value = "%" + value + "%";
                }
                SearchExpression expression = new SearchExpression(logic, property, value);
                criterion = ConvertExpression(criterion, expression);
            }

            if (criterionDate != null)
            {
                criterion = criterion != null ? Expression.And(criterion, criterionDate) : criterionDate;
            }
            foreach (SearchExpression expresson in expressions)
            {
                criterion = ConvertExpression(criterion, expresson);
            }

            return criterion;
        }

        DetachedCriteria dc = null;
        List<string> alias = new List<string>();

        /// <summary>
        /// 转换成NHibernate的查询对象DetachedCriteria
        /// </summary>
        /// <typeparam name="T">要查询的结果类型</typeparam>
        /// <returns>DetachedCriteria</returns>
        public DetachedCriteria ToDetachedCriteria<T>()
        {
            dc = DetachedCriteria.For<T>();
            ICriterion criterion = null;
            ICriterion criterionDate = null;

            object value = null;
            string property = string.Empty;

            foreach (var key in this.fields.Keys)
            {
                property = key;
                value = this.fields[key];

                if (value == null || String.IsNullOrEmpty(value.ToString()))
                {
                    continue;
                }
                bool referQuery = key.Contains(".");

                if (!Light.Utility.ReflectionHelper.TryChangeType<T>(property, ref value) && !referQuery)
                {
                    continue;
                }
                else if (value.GetType() == typeof(String))
                {
                    value = "%" + value + "%";
                }

                SearchExpression expression = new SearchExpression(logic, property, value);
                criterion = ConvertExpression(criterion, expression);
            }

            if (criterionDate != null)
            {
                criterion = criterion != null ? Expression.And(criterion, criterionDate) : criterionDate;
            }

            foreach (SearchExpression expresson in expressions)
            {
                criterion = ConvertExpression(criterion, expresson);
            }
            if (alias.Count > 0)
            {
                foreach (var name in alias)
                    dc.CreateAlias(name, name);
            }

            if (criterion != null)
            {
                dc.Add(criterion);
            }
            //日期查询条件
            if (!String.IsNullOrEmpty(DateField))
            {
                dc.Add(GetCriterionDate(DateField));
            }
            return dc;
        }


        /// <summary>
        /// 获取日期查询条件
        /// </summary>
        /// <param name="propertyName">属性名称</param>
        /// <returns></returns>
        public ICriterion GetCriterionDate(string propertyName)
        {
            ICriterion criterionDate = Expression.Sql("1=1");
            if (this.DateStart != default(DateTime) && this.DateEnd != default(DateTime))
            {

                if (DateStart > DateEnd)
                {
                    DateTime tmp = DateStart;
                    DateStart = DateEnd;
                    DateEnd = tmp;
                }
                criterionDate = Expression.Between(propertyName, DateHelper.GetStartOfDay(this.DateStart), DateHelper.GetEndOfDay(this.DateEnd));
            }
            return criterionDate;
        }


        private ICriterion ConvertExpression(ICriterion criterion, SearchExpression expresson)
        {

            object value = expresson.PropertyValue;
            string property = expresson.PropertyName;

            bool referQuery = property.Contains(".");
            if (referQuery)
            {
                string[] infos = property.Split('.');
                string customeTypeProperty = infos[0];
                string customeTypePropertyProperty = infos[1];
                if (!alias.Contains(customeTypeProperty))
                {
                    alias.Add(customeTypeProperty);
                }
            }

            bool isValueType = value.GetType().BaseType == typeof(System.ValueType);
            bool isDateTime = StringHelper.IsDateTime(value);

            ICriterion criterionObj = Expression.Like(property, value);

            switch (expresson.Operator)
            {
                case SearchOperator.Not:
                    criterionObj = Expression.Not(Expression.Eq(property, value));
                    break;

                case SearchOperator.Gt:
                    criterionObj = Expression.Gt(property, value);
                    break;

                case SearchOperator.Lt:
                    criterionObj = Expression.Lt(property, value);
                    break;

                case SearchOperator.Ge:
                    criterionObj = Expression.Ge(property, value);
                    break;

                case SearchOperator.Le:
                    criterionObj = Expression.Le(property, value);
                    break;

                default:
                    if (isDateTime)
                        criterionObj = Expression.Between(property, DateHelper.GetStartOfDay(StringHelper.ToDateTime(value)), DateHelper.GetEndOfDay(StringHelper.ToDateTime(value)));
                    else
                        criterionObj = isValueType ? Expression.Eq(property, value) : criterionObj;

                    break;
            }

            if (criterion == null)
            {
                criterion = criterionObj;
            }
            else
            {
                if (expresson.Logic == SearchLogic.And || isDateTime)
                    criterion = Expression.And(criterion, criterionObj);
                else
                    criterion = Expression.Or(criterion, criterionObj);
            }
            return criterion;
        }


        /// <summary>
        /// 搜索表达式
        /// </summary>
        class SearchExpression
        {
            public SearchExpression(SearchLogic logic, string propertyName, object propertyValue)
            {
                Logic = logic;
                Operator = SearchOperator.Eq;
                PropertyName = propertyName;
                PropertyValue = propertyValue;
            }

            public SearchExpression(SearchLogic logic, string propertyName, object propertyValue, SearchOperator optor)
                : this(logic, propertyName, propertyValue)
            {
                Operator = optor;
            }

            /// <summary>
            /// 条件逻辑
            /// </summary>
            public SearchLogic Logic { get; set; }


            /// <summary>
            /// 条件运算符
            /// </summary>
            public SearchOperator Operator { get; set; }

            /// <summary>
            /// 属性名
            /// </summary>
            public string PropertyName { get; set; }

            /// <summary>
            /// 属性值
            /// </summary>
            public object PropertyValue { get; set; }
        }

    }


}
